<template>
  <div>
    <div>
      <el-input v-model="question" placeholder="请输入问题">
        <el-button slot="append" icon="el-icon-s-promotion" @click="send()">发送</el-button>
      </el-input>
    </div>
    <div>
      <p>回答内容如下：</p>
      <div class="typing">
        <!-- <span>{{ htmlStr }}</span> -->
        <pre class="precode"><code class="hljs" v-html="highlightedCode(htmlStr)"></code><span v-if="typing" class="be_typing">__</span></pre>

      </div>
    </div>
  </div>
</template>

<script>
import hljs from 'highlight.js';
// import 'highlight.js/styles/github.css';
import '@/assets/solarized_dark.min.css'//自定义主题
let controllerList = []//controller数组
export default {
  components: {
    // highlightjs: hljsVuePlugin.component
  },
  data(){
    return{
      htmlStr:'',//回答的内容
      question:'上季度武汉销售占比？',//提的问题
      typing:true,//打字是否正在输入
    }
  },
  mounted(){
    // console.log('语言列表', hljs.listLanguages())
    this.send()
  },
  methods:{
    // 发送
    send(){
      let body = {
        input: {
          question: this.question,
          scene: 'normal',
        },
        config: {
          configurable: {
            // conversation_id: crypto.randomUUID(),
            conversation_id: this.randomUUID(),
            llm: 'gpt4'
          },
        },
      };
      let url = 'http://192.168.10.227:9000/sql/stream';
      this.htmlStr = ''
      this.typing = true
      this.chatCompletionsstream(url, body).then((res)=>{
        console.log('res', res)
      })
    },
    // chat-ai请求封装
    async chatCompletionsstream(url, body){
      let obj = {
        data:''
      }
      const bodystr = JSON.stringify(body);
      // console.log("%s\n%s\n%s", "*".repeat(100), bodystr, "*".repeat(100));
      // 想在fetch请求发送前终止它，可以使用AbortController接口。这个接口允许你创建一个信号（signal），你可以将这个信号传递给fetch请求，然后在任何时候通过调用abortController.abort()来触发信号，从而终止请求。
      // 当你调用controller.abort()方法时，如果fetch请求还没有完成，它会立即被终止，并且对应的Promise会被拒绝，你可以通过捕获Promise的catch方法来处理这种情况。
      if (controllerList.length > 0) {
        // 每次生成前终止上一个请求
        console.log('controllerList', controllerList)
        // controllerList.shift() 删除前面的对象永远留最后一条
        controllerList.shift().abort()
      }
      // 创建一个新的AbortController
      let controller = new AbortController();
      const signal = controller.signal;
      controllerList.push(controller)

      let response = null
      try{
        console.log(111)
        response = await fetch(url, {
          method: "POST",
          headers: {
            'Content-Type': 'application/json'
          },
          body: bodystr,
          signal
        })
        // console.log(response.status, response.statusText);
        obj.status = response.status
        if (response.status !== 200) {
          const text = await response.text();
          this.htmlStr = ''
          throw text;
        }
        const textDecoder = new TextDecoder("utf-8");
        const reader = response.body.getReader();
        do {
          const { done, value } = await reader.read();
          const message = textDecoder.decode(value);
          // console.log('do-while', message)
          obj.message = message
          if (message.startsWith('event: data')) {
            let matchers = message.match(/data: (.*)/g); // 可能多条event: data 一起返回
            // console.log('process', process, process.stdout)
            try {
              if (matchers) {
                for (let matcher of matchers) {
                  // process.stdout.write(eval(matcher));//node控制台输出
                  // console.log('matcher', eval(matcher))
                  obj.data += eval(matcher)
                  this.htmlStr += eval(matcher)
                }
              }
            } catch (e) {
              // process.stdout.write(message);//node控制台输出
              console.log('catch-message', message)
            }
          } else if (message.startsWith('event: end')) {
            this.typing = false
          }
          if (done) {
            break;
          }
        }while (true);
      }catch(err){
        console.log(222, 'request abort is not network', err)
        controller = null
        this.htmlStr = ''
      }finally{
        // 请求完之后将controller置为空
        controller = null
        console.log(333)
      }
      console.log('obj',obj)
      return response
    },
    // 生成UUID
    randomUUID() {
      const hexDigits = '0123456789abcdef';
      let uuid = '';
      for (let i = 0; i < 36; i++) {
        if (i === 8 || i === 13 || i === 18 || i === 23) {
          uuid += '-';
        } else if (i === 14) {
          uuid += '4';
        } else if (i === 19) {
          uuid += hexDigits[(Math.floor(Math.random() * 4) + 8)];
        } else {
          uuid += hexDigits[Math.floor(Math.random() * 16)];
        }
      }
      return uuid;
    },
    /** 高亮显示 */
    highlightedCode(code) {
      // const vmName = key.substring(key.lastIndexOf("/") + 1, key.indexOf(".vm"));
      // var language = vmName.substring(vmName.indexOf(".") + 1, vmName.length);
      // let language = 'markdown'
      let language = 'sql'
      const result = hljs.highlight(language, code || "", true);
      return result.value || '&nbsp;';
    },
  }
}
</script>

<style lang="scss" scoped>
.typing{
  position: relative;
  font-size: 14px;
  .be_typing{
    animation: fadeIn 1s ease-in infinite;
  }
  .precode{
    white-space: pre-wrap;
  }
}
/* 定义一个淡入的关键帧 */
@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
</style>
